package cn.calm.commons.dao.impl;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.log4j.Logger;

import cn.calm.commons.dao.IBaseDao;
import cn.calm.commons.dao.QueryConditionBuilder;
import cn.calm.commons.entity.BaseEntity;
import cn.calm.commons.entity.LogicallyDelete;
import cn.calm.commons.entity.PageMode;

public class BaseDaoImpl implements IBaseDao {
	private static final Logger log = Logger.getLogger(BaseDaoImpl.class);
	private EntityManager session;

	@Override
	public <T extends BaseEntity> void insert(T t) {
		log.debug("insert start");
		if (!getSession().getTransaction().isActive()) {
			getSession().getTransaction().begin();
		}
		if (t instanceof LogicallyDelete) {
			((LogicallyDelete) t)
					.setDeleteClass(LogicallyDelete.DELETECLASS_NORMAL);
		}
		getSession().persist(t);
		getSession().getTransaction().commit();
		log.debug("insert end");
	}

	@Override
	public <T extends BaseEntity> void modify(T t) {
		log.debug("modify start");
		getSession().getTransaction().begin();
		getSession().merge(t);
		getSession().getTransaction().commit();
		log.debug("modify end");
	}

	// @Override
	// public <T extends BaseEntity> void merge(T t) {
	// log.debug("merge start");
	// getSession().getTransaction().begin();
	// getSession().merge(t);
	// getSession().getTransaction().commit();
	// log.debug("merge end");
	// }

	@Override
	public <T extends BaseEntity> void delete(T t) {
		log.debug("delete start");
		getSession().getTransaction().begin();
		getSession().remove(t);
		getSession().getTransaction().commit();
		log.debug("delete end");
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends BaseEntity> List<T> list(
			final Class<? extends BaseEntity> clazz,
			QueryConditionBuilder<T> query) {
		log.debug("list start");
		try {
			CriteriaBuilder cb = getSession().getCriteriaBuilder();
			CriteriaQuery<? extends BaseEntity> typeQuery = cb
					.createQuery(clazz);
			Root<? extends BaseEntity> root = typeQuery.from(clazz);
			query.query(cb, typeQuery,root);
			return (List<T>) getSession().createQuery(typeQuery)
					.getResultList();
		} finally {
			log.debug("list end");
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends BaseEntity> T load(Class<?> clazz, Serializable pk) {
		log.debug("load start");
		try {
			T t = (T) getSession().find(clazz, pk);
			if (t instanceof LogicallyDelete) {
				LogicallyDelete ld = (LogicallyDelete) t;
				if (LogicallyDelete.DELETECLASS_DELETED.equals(ld
						.getDeleteClass())) {
					return null;
				}
			}
			return t;
		} finally {
			log.debug("load end");
		}
	}

	public <T extends BaseEntity> List<T> listInByProperty(
			final Class<T> clazz, final String property,
			final String[] dependencies) {
		log.debug("listInByProperty start");
		try {
			CriteriaBuilder cb = getSession().getCriteriaBuilder();
			CriteriaQuery<T> query = (CriteriaQuery<T>) cb.createQuery(clazz);
			Root<T> root = query.from(clazz);

			if (dependencies != null && dependencies.length > 0) {
				Predicate[] predicates = new Predicate[dependencies.length];
				Path<String> pro = root.get(property);
				for (int i = 0; i < dependencies.length; i++) {
					Predicate pre = cb.equal(pro, dependencies[i]);
					predicates[i] = pre;
				}
				query.where(cb.or(predicates));
			}
			TypedQuery<T> typedQuery = getSession().createQuery(query);
			return typedQuery.getResultList();
		} finally {
			log.debug("listInByProperty end");
		}
	}

	// @SuppressWarnings("unchecked")
	// @Override
	// public <T extends BaseEntity> List<T> listByProperty(final Class<?>
	// clazz,
	// final String property, final Object value) {
	// log.debug("listInByProperty start");
	// try {
	// CriteriaBuilder cb=getSession().getCriteriaBuilder();
	// CriteriaQuery<?> query= cb.createQuery(clazz);
	// Root<?> root=query.from(clazz);
	// query.where(cb.equal(root.get(property), value));
	// return (List<T>) getSession().createQuery(query).getResultList();
	// } finally {
	// log.debug("listInByProperty end");
	// }
	// }

	// @Override
	// public String getMax(final Class<?> clazz, final String property) {
	// log.debug("listInByProperty start");
	// try {
	// return null;
	// } finally {
	// log.debug("listInByProperty end");
	// }
	// }

	@Override
	public void exceHQL(String string, Map<String, Object> parameter) {
		log.debug("exceHQL start");
		// Session
		// session=getHibernateTemplate().getSessionFactory().openSession();
		// Query q=session.createQuery(string);
		// if(parameter!=null){
		// for(String key:parameter.keySet()){
		// q.setParameter(key, parameter.get(key));
		// }
		// }
		// q.executeUpdate();
		log.debug("exceHQL end");
	}

	public <T extends BaseEntity> PageMode listPage(final PageMode pageMode,
			final Class<? extends BaseEntity> clazz,
			final QueryConditionBuilder<T> query) {
		log.debug("listPage start");
		try {

			CriteriaBuilder cb = getSession().getCriteriaBuilder();
			CriteriaQuery<? extends BaseEntity> queryEntity = cb
					.createQuery(clazz);
			query.query(cb, queryEntity,queryEntity.from(clazz));
			TypedQuery<? extends BaseEntity> typedQuery = getSession()
					.createQuery(queryEntity);
			typedQuery.setFirstResult(pageMode.getStartRow());
			typedQuery.setMaxResults(pageMode.getPageSize());
			List<? extends BaseEntity> list = typedQuery.getResultList();
			pageMode.setData(list);
			CriteriaQuery<Long> queryCount = cb.createQuery(Long.class);
			Root<? extends BaseEntity> root = queryCount.from(clazz);
			queryCount.select(cb.count(root));
			Long size = getSession().createQuery(queryCount).getSingleResult();
			pageMode.setTotalPage((size + pageMode.getPageSize() - 1)
					/ pageMode.getPageSize());
			pageMode.setTotalCount(size);
			return pageMode;
		} finally {
			log.debug("listPage end");
		}
	}

	@PersistenceContext
	public EntityManager getSession() {
		return session;
	}

	public void setSession(EntityManager session) {
		this.session = session;
	}

}
